iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
1

參考資料

动态组件 & 异步组件 - Vue.js


我覺得自己文章標題取有夠爛,沒什麼美感又超白話哈哈,這篇是個獨立的單元講述動態組建的建立和切換,沒有使用到之前的程式碼。

使用情境參考

按鈕1: 按下取得部落格已發佈的文章

按鈕2: 按下取得垃圾桶文章

開始製作 (粗糙版) : 雖不同於 Vue.js 官網所提供的範例,但是透過以下的步驟,將可以迅速地知道怎麼切換組件。

先準備需要的材料

  • 一個存有已發布文章的小組件
const postedArticle = {
  name: 'postedArticle',
  template: `
    <div>
      <div v-for="post in posts" :key="post.id">{{ post.article }}</div>
    </div>
  `,
  data () {
    return {
      posts: [{ article: '正常文章1', id: 1 }, { article: '正常文章2', id: 2 }]
    }
  }
}
  • 一個存有垃圾文章的小組件
const postedArticle = {
  name: 'postedArticle',
  template: `
    <div>
      <div v-for="post in posts" :key="post.id">{{ post.article }}</div>
    </div>
  `,
  data () {
    return {
      posts: [{ article: '垃圾文章1', id: 1 }, { article: '垃圾文章2', id: 2 }]
    }
  }
}

重要提示,本教學僅為製作步驟及原理參考,且僅提供思路,不代表最佳實踐

兩個組件結構一模一樣 ,這裡只是為文章方便,所以寫成小組件模擬文章的模式,實際上較好的作法並不是寫成這種小組件,因為template的部分沒有語法高量以及emmet的支援 ( 維護上比較麻煩 ) ,而是應該將兩個組件個別做為不同的 ( 或相同的,看怎麼設計,比如文章的部分應該用非同步呼叫 API 的方式取得文章列表後放入 posts ) .vue 檔案來進行更加細部的功能封裝。

  • 兩個按鈕分別切換組件,以及組件的設置
<button type="button" @click = "currentComponent=postedArticle">取得正常文章</button>
<button type="button" @click = "currentComponent=trashArticle">取得垃圾文章</button>
<component :is="currentComponent">

透過按鈕註冊click事件來改變 currentComponent ,即可進行組件的切換,而這邊是整段代碼的設定:

<template>
  <button type="button" @click = "currentComponent=postedArticle">取得正常文章</button>
  <button type="button" @click = "currentComponent=trashArticle">取得垃圾文章</button>
  <component :is="currentComponent"></component>
</template>

<script>
const postedArticle = { 以下略 }
const trashArticle= { 以下略 }

export default {
  name: 'testCase',
  data () {
    return {
      currentComponent: postedArticle, // 這裡預設一開始顯現普通文章
      postedArticle: postedArticle,
      trashArticle: trashArticle
    }
  }
}
</script>

關鍵:
使用 <component> tag 並綁定 :is="自定義的組件名稱" 隨後透過我們自己的任意方式且在任意時機變更 自定義的組件名稱 以達成組件切換。

後續實作思路:

點下取得不同文章時,若我們用call API的方式實作,可以在等待的途中加入類似Loading的特效,讓使用者知道系統正在讀取中,避免因文章的讀取空白時間過長,讓使用者誤會系統沒反應。


開始製作 (官網改寫版) : 在知道關鍵點後,我們就可以來一步步拆解官網提供的範例 並改寫成我們的版本,假如忘記關鍵是什麼,請稍微往上再看一下關鍵的敘述...

使用情境參考

同粗糙版

一樣先準備需要的材料

  • 一開始一樣先準備小組件,這次準備三個,內容都先空。
const postedArticleForVersion = {}
const trashArticleForVersion = {}
const draftForVersion = {}
  • 再來分別準備好按鈕和標籤tabs ,由於按鈕是v-for="tab in tabs" 來渲染,所以按鈕數量將取決於tabs陣列的長度。
<template>
  <div>
	<button
      v-for="tab in tabs"
      v-bind:key="tab"
      v-on:click="currentTab = tab"
	>
	  {{ tab }}
    </button>
  </div>
</template>

<script>
export default {
  data () {
	return {
	  currentTab: "正常文章",            // 預設顯示正常文章
	  tabs: ['正常文章', '廢棄文章']
	}
  }
}
</script>

// 做到以上結束,會有兩個按鈕
  • 還記得下面粗糙版的版本嗎 ?這段的功能有點像是是土法煉鋼的方法,一個一個地指定按鈕點了之後改變currentComponent所存放的組件。
<button type="button" @click = "currentComponent=postedArticle">取得正常文章</button>
<button type="button" @click = "currentComponent=trashArticle">取得垃圾文章</button>
<component :is="currentComponent">

// 我們這段類似官網,比較自動化,如此可以透過不斷增加tabs的長度來新增按鈕, v-for="tab in tabs" 這是Vue中用來遍歷陣列內容的方法,tab從第0個元素開始
// 而v-for是掛在button上,所以最終會依照tabs的長度分別渲染該次迴圈值給 {{ tab }} , 到這裡為止你的畫面依照tabs陣列的順序變成2個按鈕了

插播注意 ! 官網範例中使用以下computed 計算屬性來偵聽 button 點按改變currentTab 後的方式來切換組件,我們的版本不會使用這樣的方式。

<template>
	<component v-bind:is="currentTabComponent" class="tab"></component>
</template>

<script>
// ...vue實例內
computed: {
  currentTabComponent: function() {
    return "tab-" + this.currentTab.toLowerCase();
  }
}
</script>

結束插播,回到本範例

  • 接下來,依照官網的例子,按下按鈕後必須切換組件,特別是第一個按鈕按下去後有出現子選項可點選並顯示文章內容,回到我們的邏輯,我們就將使用情境改為按下正常文章,會依序出現正常文章1,正常文章2,正常文章3這樣的子選項,內容就依序給他文章內容1,文章內容2, 文章內容3, 廢棄文章和草稿通通都是一樣的邏輯。
  • 接下來把 button v-for 改為
<button v-for="tab in tabs"             
  :key="tab.id"
  @click="currentComponentForVersion = tab.id" >
  {{ tab.title }}                      
</button>
  • 再配合v-for 將 tabs 改為
tabs: [
  { title: '正常文章', id: 'postedArticleForVersion' }, 
  { title: '廢棄文章', id: 'trashArticleForVersion' }, 
  { title: '草稿', id: 'draftForVersion' }
]
  • 現在你的畫面變三個按鈕,並且之前 Vue 實例內的 data 屬性 currentTab: "正常文章", 已經沒用了,請刪除之。

  • 我們這裡切換組件的方法並不像官網一樣使用 computed 屬性偵測更換,而是在渲染按鈕的時候將變更組件的動作如同粗糙版一樣綁在click事件中,個人認為比額外寫在 computed 中還要具有可讀性。

  • 接著在button下方加入關鍵 <component :is="currentComponentForVersion"></component> ,現在可以成功的切換組件了。咦 ?但是現在按了什麼反應都沒有啊 ,別急,還記得我們三個小組件現在還是空白的嗎?現在我們要來開始撰寫組件內容了,以下是我們要使用的組件內容,先看一組就好。

const postedArticleForVersion = { 
  name: 'postedArticleForVersion',
  template: `
    <div class="container-article">
      <div class="container-list">
        <div v-for="post in posts" @click="articleSelected = post.article">
          {{ post.title }} 
        </div>
      </div>
      <div class="container-article__show">
        {{ articleSelected }}
      </div>
    </div>
  `,
  data () {
    return {
      posts: [
        { title: '正常文章1', article: '正常文章內容1', id: 1, show: false },
        { title: '正常文章2', article: '正常文章內容2', id: 2, show: false }, 
        { title: '正常文章3', article: '正常文章內容3', id: 3, show: false }
      ],
      articleSelected: null
    }
  }
}
  • 上方這一組是正常文章的組件內容,div.container-article 包裹者兩個分別為
  • div.container-listdiv.container-article__show
  • div.container-list 內部有 <div v-for="post in posts"{{ post.title }} 負責渲染代表標題的div區塊,而@click="articleSelected = post.article 的部分負責指定當下迴圈索引寫入的文章內容。
  • div.container-article__show 負責顯示使用者選擇的標題內文

注意 ,div.container-list 所渲染出的 div 區塊,可以是<a>、或者使用<li> 包裹在有序或無序清單內或者其他方案,此處僅僅為展示內容方便,因此通通使用 div 標籤,其具體結構及CSS請自行依照情況不同斟酌變化。

  • 了解一個組件的結構後,接下來完成另外廢棄文章和草稿的組件,它們的不同僅僅在 posts 的部分而已。
const trashArticleForVersion = { 
  name: 'trashArticleForVersion',
  template: `
    <div class="container-article">
      <div class="container-list">
        <div v-for="post in posts" @click="articleSelected = post.article">
          {{ post.title }} 
        </div>
      </div>
      <div class="container-article__show">
        {{ articleSelected }}
      </div>
    </div>
  `,
  data () {
    return {
      posts: [
        { title: '垃圾文章1', article: '垃圾文章內容1', id: 1 }, 
        { title: '垃圾文章2', article: '垃圾文章內容2', id: 2 }, 
        { title: '垃圾文章3', article: '垃圾文章內容3', id: 1 }
      ],
      articleSelected: null
    }
  }
}

const draftForVersion = { 
  name: 'trashArticleForVersion',
  template: `
    <div class="container-article">
      <div class="container-list">
        <div v-for="post in posts" @click="articleSelected = post.article">
          {{ post.title }} 
        </div>
      </div>
      <div class="container-article__show">
        {{ articleSelected }}
      </div>
    </div>
  `,
  data () {
    return {
      posts: [
        { title: '草稿1', article: '草稿內容1', id: 1 }, 
        { title: '草稿2', article: '草稿內容2', id: 2 }, 
        { title: '草稿3', article: '草稿內容3', id: 1 }
      ],
      articleSelected: null
    }
  }
}
  • 把三個組件的內容補完後,接下來就會得到如下可切換文章分類,且可點選不同文章內容的功能了。

    並且應該發現到切換組件的關鍵就算到的官方改寫版,也依然是文始提及的:

    關鍵:
    使用 <component> tag 並綁定 :is="自定義的組件名稱" 隨後透過我們自己的任意方式且在任意時機變更 自定義的組件名稱 以達成組件切換。

你可以在這裡得到本文件範例 ,範例內容包含了

  1. 粗糙版的組件切換
  • 使用 data 屬性的方式代替註冊在 component 中
  • 沒有使用 v-for
  1. 官方改寫版
  • 定義完組件後註冊在 component 中
  • 使用 v-for
  • 小組件的細部內容
  1. 最後別忘了,可以使用<keep-alive></keep-alive><component :is></component> 包住 ,如此之前的資料就會被快取起來,再點回去的時候資料就不會重新渲染變空了

本教程到此結束,請再次注意,一般來說不會在一個頁面像這樣定義這麼多小組件,本教程不代表最佳做法,僅僅展示如何切換組件。

https://codepen.io/fiftybillionHuang/pen/gOrWQEw


沒事也可以逛逛我們其他團隊成員的文章啦 ~~
eien_zheng: 前端小嘍嘍的Golang學習旅程_The journey of learning Golang 系列
PollyPO技術: 前端設計轉前端工程師-JS踩坑雜記 30 天 系列
阿電: 忍住不打牌位,只要30天VueJS帶你上A牌 系列
喬依司: 實作經典 JavaScript 30 系列


上一篇
Day 05: 具名插槽 + 作用域 + CLI 前哨站之拆解 BootstrapVue Table cell() 假象
下一篇
Day 07: Vue-CLI 先認識專案目錄
系列文
Vue CLI + Firebase 雲端資料庫 30天打造簡易部落格及後臺管理30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言